// 实现日志功能
#pragma once
#include <iostream>
#include <string>
#include <stdarg.h>
#include <fstream>
#include <filesystem>
#include <unordered_map>
#include <memory>
#include <mutex>
// #include <chrono>
#include "./util.hpp"
#include "./CoR.hpp"

namespace ns_log
{
    using namespace ns_util;
    using namespace CoR;
    enum // 匿名枚举，类型可以是整型
    {
        Debug,
        Info,
        Warning,
        Error,
        Fatal
    };
    const std::string defaultpath = "../comm/Log/"; // 进程工作路径的相对路径
    class LogUtil                                   // 设计单例模式的日志功能
    {
        LogUtil(const LogUtil &obj) = delete;
        const LogUtil &operator=(const LogUtil &obj) = delete;

    private:                                                     // 单例私有构造函数
        std::string _filepath;                                   // 文件路径：文件名在后面体现
        std::string _suffix = ".txt";                            // 文件后缀
        std::unordered_map<std::string, std::ofstream> _filemap; // 存储映射
        std::mutex _mtx;                                         // 线程安全锁

    private:
        LogUtil(const std::string &filepath = defaultpath)
            : _filepath(filepath)
        {
            // 检查文件是否存在
            //  无法保证，路径是正确的/路径是已经存在的。
            //  所以，在进行执行之前，我们在构造该任务点的时候，就把路径创建成功 —> C++17语法
            if (std::filesystem::exists(_filepath)) // 如果当前文件路径存在
                return;                             // 直接返回

            // 文件路径不存在
            //  创建文件目录
            try
            {
                std::filesystem::create_directories(_filepath); // 创建文件目录
            }
            catch (std::filesystem::filesystem_error const &e)
            {
                std::cerr << e.what() << '\n';
            }
        }

    public:
        // 析构时确保所有缓冲区数据写入文件
        ~LogUtil()
        {
            for (auto &[level, ofs] : _filemap)
            {
                if (ofs.is_open())
                {
                    ofs.flush();
                    ofs.close();
                }
            }
        }

    public:
        static LogUtil &GetInstance()
        {
            static LogUtil lg; // C++之后这个是线程安全的
            return lg;
        }

        void Log(const std::string &level, const std::string &filename, int lines, const char *format, ...)
        {
            // 实现信息的组装
            std::string message;

            // 1、[level]
            message += '[';
            message += level;
            message += "][";

            // 2、文件名称
            message += filename;
            message += "][";

            // 3、信息行数
            message += std::to_string(lines);
            message += ']';

            // 4、时间
            message += TimeUtil::GetFormatTime(); // 获取时间

            // 5、格式化消息
            // 5.1：将格式化信息提取出来
            va_list s;
            va_start(s, format);
            char msg[1024];
            vsnprintf(msg, sizeof(msg), format, s);
            va_end(s);
            std::string information = msg;
            // 5.2 追加到message上
            message += "# ";
            message += information;

            // 6、分文件写入到日志文件中：目录./Log
            WriteFile(message, level);
        }

    private:
        void CloseFile(const std::string &level) // 提供一个成员方法能够主动关闭文件
        {
            std::lock_guard<std::mutex> lock(_mtx);
            auto it = _filemap.find(level);
            if (it != _filemap.end() && it->second.is_open())
            {
                it->second.flush();
                it->second.close();
                _filemap.erase(it); // 使用迭代器删除更安全
            }
        }
        void OpenFile(const std::string &level) // 提供一个成员方法能够主动打开一个文件
        {
            std::lock_guard<std::mutex> lock(_mtx);
            ExistFile(level);
        }
        void WriteFile(const std::string &mes, const std::string &level)
        // 我们也可以将这个函数写入责任链中
        {
            // 根据传入的日志等级，写入不同的文件
            //  路径："../Log/"
            //  文件名："level.txt"
            {
                std::lock_guard<std::mutex> lock(_mtx);

                // 判断文件是否存在：如果存在，就直接写入；如果不存在创建
                if (ExistFile(level))
                {
                    _filemap[level] << mes << std::endl;
                }
            }
            // 对传入的文件进行处理TODO
            Enter he(_filepath, level + _suffix);

            // 责任链处理之前
            //  1. 删除原来的映射
            CloseFile(level); // 内部加锁了，避免死锁
            // 2. 重命名打包
            {
                std::lock_guard<std::mutex> lock(_mtx);
                he.Run();
            }
            // 3. 创建新的映射
            OpenFile(level);
        }

        bool ExistFile(const std::string &level)
        {
            auto it = _filemap.find(level);
            if (it == _filemap.end()) //
            {
                // 构建完整路径
                std::string file = _filepath + level + _suffix;
                // 以追加模式打开，不自动刷新缓冲区
                // 追加：避免覆盖历史日志；二进制：兼容多平台换行
                std::ofstream ofs(file, std::ios::app | std::ios::binary);
                if (!ofs.is_open())
                {
                    std::cerr << "file " << file << " open fail..." << std::endl;
                    return false;
                }
                _filemap.emplace(level, std::move(ofs));
            }
            return true;
        }
    };
}

#define LOG(level, format, ...) ns_log::LogUtil::GetInstance().Log(#level, __FILE__, __LINE__, format, ##__VA_ARGS__)